package com.lfs.notify.controller;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.lfs.base.util.CollectionUtils;
import com.lfs.interfaces.model.dto.OrderCharge;
import com.lfs.interfaces.model.zfb.ZFBNotifyParams;
import com.lfs.interfaces.order.service.OrderBaseService;
import com.lfs.notify.service.zfb.service.ZFBNotifyService;
import com.lfs.notify.util.RequestToMapUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@RestController
@RequestMapping(value = "/aliPayNotify")
public class ZFBNotifyController {

    private static final Logger logger = LoggerFactory.getLogger(ZFBNotifyController.class);

    @Autowired
    private ZFBNotifyService zfbNotifyService;

    @Resource
    private OrderBaseService orderBaseService;

    @Value("${bc.alipayPublickKey}")
    private String publicKey;

    @Value("${bc.developAppId}")
    private String appId;

    private ExecutorService executorService = Executors.newFixedThreadPool(20);

    /**
     * <pre>
     * 第一步:验证签名,签名通过后进行第二步
     * 第二步:按一下步骤进行验证
     * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
     * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
     * 3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
     * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。
     * 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。
     * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
     * </pre>
     *
     * @param request
     * @return
     */
    @RequestMapping("/notify")
    @ResponseBody
    public String notifyZFBCharge(HttpServletRequest request) {
        logger.info("######开始执行支付宝通知回调######");
        Map<String, String> params = RequestToMapUtil.convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中
        String paramsJson = JSON.toJSONString(params);
        logger.info("支付宝回调，{}", paramsJson);
        try {
            // 调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, publicKey, "utf-8", "RSA2");
            if (signVerified) {
                logger.info("支付宝回调签名认证成功");
                List<OrderCharge> orderChargeList = this.check(params);
                // 另起线程处理业务
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        ZFBNotifyParams param = RequestToMapUtil.buildAlipayNotifyParam(params);

                        // 处理支付后的回调逻辑
                        try {
                            String respString = zfbNotifyService.notifyZFB(param,orderChargeList);
                            logger.info("回调处理结果：params=[" + paramsJson + "] result=[" + respString + "]");

                        } catch (Exception e) {
                            logger.error("支付宝回调业务处理报错,params:" + paramsJson, e);
                        }
                    }
                });
                // 如果签名验证正确，立即返回success，后续业务另起线程单独处理
                // 业务处理失败，可查看日志进行补偿，跟支付宝已经没多大关系。
                return "success";
            } else {
                logger.info("支付宝回调签名认证失败，signVerified=false, paramsJson:{}", paramsJson);
                return "failure";
            }
        } catch (AlipayApiException e) {
            logger.error("支付宝回调签名认证失败,paramsJson:{},errorMsg:{}", paramsJson, e.getMessage());
            return "failure";
        }
    }

    /**
     * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
     * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
     * 3、校验通知中的seller_id（或者seller_email)是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
     * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。
     * 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。
     * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
     *
     * @param params
     * @throws AlipayApiException
     */
    private List<OrderCharge> check(Map<String, String> params) throws AlipayApiException {
        String orderNo = params.get("out_trade_no");

        OrderCharge order = new OrderCharge();
        order.setOrderNo(orderNo);

        // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，

        List<OrderCharge> orders = orderBaseService.getValidOrderForConfirmByBuscodeAndOrderNo(order);

        if (CollectionUtils.isEmpty(orders)) {
            throw new AlipayApiException("out_trade_no错误");
        }

        // 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
        long total_amount = new BigDecimal(params.get("total_amount")).multiply(new BigDecimal(100)).longValue();
        if (total_amount != orders.get(0).getChargeMoney().multiply(new BigDecimal(100)).longValue()) {
            throw new AlipayApiException("error total_amount");
        }

        // 3、校验通知中的seller_id（或者seller_email)是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
        // 第三步可根据实际情况省略

        // 4、验证app_id是否为该商户本身。
        if (!params.get("app_id").equals(appId)) {
            throw new AlipayApiException("app_id不一致");
        }
        return orders;
    }

}
